a tool for shared writing and social publishing
1import { z } from "zod";
2import { makeRoute } from "../lib";
3import type { Env } from "./route";
4import { getIdentityData } from "actions/getIdentityData";
5import { tinybird } from "lib/tinybird";
6
7export type GetPublicationAnalyticsReturnType = Awaited<
8 ReturnType<(typeof get_publication_analytics)["handler"]>
9>;
10
11export const get_publication_analytics = makeRoute({
12 route: "get_publication_analytics",
13 input: z.object({
14 publication_uri: z.string(),
15 from: z.string().optional(),
16 to: z.string().optional(),
17 path: z.string().optional(),
18 }),
19 handler: async (
20 { publication_uri, from, to, path },
21 { supabase }: Pick<Env, "supabase">,
22 ) => {
23 const identity = await getIdentityData();
24 if (!identity?.atp_did || !identity.entitlements?.publication_analytics) {
25 return { error: "unauthorized" as const };
26 }
27
28 // Verify the user owns this publication
29 const { data: publication } = await supabase
30 .from("publications")
31 .select("*, publication_domains(*)")
32 .eq("uri", publication_uri)
33 .single();
34
35 if (!publication || publication.identity_did !== identity.atp_did) {
36 return { error: "not_found" as const };
37 }
38
39 const domain = publication.publication_domains?.[0]?.domain;
40 if (!domain) {
41 return {
42 result: { traffic: [], topReferrers: [], topPages: [] },
43 };
44 }
45
46 const [trafficResult, referrersResult, pagesResult] = await Promise.all([
47 tinybird.publicationTraffic.query({
48 domain,
49 ...(from ? { date_from: from } : {}),
50 ...(to ? { date_to: to } : {}),
51 ...(path ? { path } : {}),
52 }),
53 tinybird.publicationTopReferrers.query({
54 domain,
55 ...(from ? { date_from: from } : {}),
56 ...(to ? { date_to: to } : {}),
57 ...(path ? { path } : {}),
58 limit: 10,
59 }),
60 tinybird.publicationTopPages.query({
61 domain,
62 ...(from ? { date_from: from } : {}),
63 ...(to ? { date_to: to } : {}),
64 limit: 20,
65 }),
66 ]);
67
68 return {
69 result: {
70 traffic: trafficResult.data,
71 topReferrers: referrersResult.data,
72 topPages: pagesResult.data,
73 },
74 };
75 },
76});